//=============================================================================
//
//		Copyright (C) 1995,1996 by Paul S. McCarthy and Eric Sunshine.
//				Written by Paul S. McCarthy and Eric Sunshine.
//							All Rights Reserved.
//
//		This notice may not be removed from this source code.
//
//		This object is included in the MiscKit by permission from the authors
//		and its use is governed by the MiscKit license, found in the file
//		"License.rtf" in the MiscKit distribution.	Please refer to that file
//		for a list of all applicable permissions and restrictions.
//
//=============================================================================
//-----------------------------------------------------------------------------
// DirArray.m
//
//		An extensible array of directory entries.
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// $Id$
// $Log$
//-----------------------------------------------------------------------------
#import "DirArray.h"
#import <appkit/appkit.h>
#import <assert.h>


@implementation DirArray

//-----------------------------------------------------------------------------
// safeStrdup
//-----------------------------------------------------------------------------
- (char*) safeStrdup:(char const*)s
	{
	char* t = 0;
	if (s != 0)
		t = NXCopyStringBufferFromZone( s, [self zone] );
	return t;
	}


//-----------------------------------------------------------------------------
// safeFree
//-----------------------------------------------------------------------------
- (void) safeFree:(void*)p
	{
	if (p != 0)
		NXZoneFree( [self zone], p );
	}


//-----------------------------------------------------------------------------
// dirEntryCons
//-----------------------------------------------------------------------------
- (void) dirEntryCons: (DirEntry*) de
	name: (char const*) n
	path: (char const*) path
	{
	int rc;
	memset( de, 0, sizeof(*de) );

	de->dir = self;
	de->name = [self safeStrdup:n];

	if ((rc = lstat( path, &(de->st) )) == 0)
		{
		if ((de->st.st_mode & S_IFMT) == S_IFLNK)		// soft-link?
			{
			char symbuf[ FILENAME_MAX + 1 ];
			int cc;
			cc = readlink( path, symbuf, FILENAME_MAX );
			if (cc >= 0)
				{
				symbuf[ cc ] = '\0';
				de->soft_link = [self safeStrdup:symbuf];
				rc = stat( path, &(de->st) );
				}
			else
				rc = -1;
			}
		}

	if (rc != 0)
		de->err = errno;
	}


//-----------------------------------------------------------------------------
// dirEntryDestroy:
//-----------------------------------------------------------------------------
- (void) dirEntryDestroy:(DirEntry*)de
	{
	if (de->image != 0)
		[de->image free];
	[self safeFree:de->name];
	[self safeFree:de->soft_link];
	}


//-----------------------------------------------------------------------------
// entryAt:
//-----------------------------------------------------------------------------
- (DirEntry const*)entryAt:(int)n
	{
	assert( 0 <=n && n < num_entries );
	return (entries + n);
	}


//-----------------------------------------------------------------------------
// isDir:
//-----------------------------------------------------------------------------
- (BOOL) isDir:(DirEntry const*)de
	{
	return ((de->st.st_mode & S_IFMT) == S_IFDIR);
	}


//-----------------------------------------------------------------------------
// isDirAt:
//-----------------------------------------------------------------------------
- (BOOL) isDirAt:(int)n
	{
	return [self isDir:[self entryAt:n]];
	}


//-----------------------------------------------------------------------------
// loadImage:
//-----------------------------------------------------------------------------
- (NXImage*) loadImage:(DirEntry*)de
	{
	char buff[ FILENAME_MAX + 1 ];
	char const* const dir_part = de->dir->name;
	int const dirlen = strlen( dir_part );
	memcpy( buff, dir_part, dirlen );
	strcpy( buff + dirlen, de->name );
	de->image = [[Application workspace] getIconForFile: buff];
	[de->image setScalable: YES];
	return de->image;
	}


//-----------------------------------------------------------------------------
// getImage:
//-----------------------------------------------------------------------------
- (NXImage*) getImage:(DirEntry const*)de
	{
	if (de->image == 0)
		[self loadImage:(DirEntry*)de];
	return de->image;
	}


//-----------------------------------------------------------------------------
// getImageAt:
//-----------------------------------------------------------------------------
- (NXImage*) getImageAt:(int)n
	{
	return [self getImage:[self entryAt:n]];
	}


//-----------------------------------------------------------------------------
// init
//-----------------------------------------------------------------------------
- init
	{
	[super init];
	total_bytes = 0;
	num_entries = 0;
	max_entries = 0;
	entries = 0;
	return self;
	}


//-----------------------------------------------------------------------------
// empty
//-----------------------------------------------------------------------------
- (void) empty
	{
	DirEntry* p;
	for (p = entries + num_entries; p != entries; )
		[self dirEntryDestroy:--p];
	num_entries = 0;
	total_bytes = 0;
	}


//-----------------------------------------------------------------------------
// free
//-----------------------------------------------------------------------------
- free
	{
	[self empty];
	[self safeFree:name];
	[self safeFree:entries];
	return [super free];
	}


//-----------------------------------------------------------------------------
// count
//-----------------------------------------------------------------------------
- (int) count
	{
	return num_entries;
	}


//-----------------------------------------------------------------------------
// totalBytes
//-----------------------------------------------------------------------------
- (int) totalBytes
	{
	return total_bytes;
	}


//-----------------------------------------------------------------------------
// remove:
//-----------------------------------------------------------------------------
- (void) remove:(int)n
	{
	if (0 <= n && n < num_entries)
		{
		DirEntry* p = entries + n;
		total_bytes -= p->st.st_size;
		num_entries--;
		[self dirEntryDestroy:p];
		if (n < num_entries)
			memmove( p, p + 1, (num_entries - n) * sizeof(*p) );
		}
	}


//-----------------------------------------------------------------------------
// expand
//-----------------------------------------------------------------------------
- (void) expand
	{
	DirEntry* p = entries;
	int N = max_entries;
	if (N == 0)
		{
		N = 16;
		p = (DirEntry*) NXZoneMalloc( [self zone], N * sizeof(*p) );
		}
	else
		{
		N += N;
		p = (DirEntry*) NXZoneRealloc( [self zone], p, N * sizeof(*p) );
		}
	max_entries = N;
	entries = p;
	}


//-----------------------------------------------------------------------------
// addName:path:
//-----------------------------------------------------------------------------
- (void) addName:(char const*)n path:(char const*)fullpath
	{
	DirEntry* p;
	if (num_entries >= max_entries)
		[self expand];
	p = entries + num_entries++;
	[self dirEntryCons:p name:n path:fullpath];
	total_bytes += p->st.st_size;
	}


//-----------------------------------------------------------------------------
// loadPath:showHidden:
//-----------------------------------------------------------------------------
- (int) loadPath:(char const*)path showHidden:(BOOL)showHidden
	{
	int rc = 0;
	int dirlen;
	DIR* dirp;
	char namebuff[ FILENAME_MAX + 1 ];

	dirlen = strlen( path );
	strcpy( namebuff, path );
	if (dirlen == 0 || namebuff[ dirlen - 1 ] != '/')
		{
		namebuff[ dirlen++ ] = '/';
		namebuff[ dirlen ] = '\0';
		}

	[self empty];
	[self safeFree:name];
	name = [self safeStrdup:namebuff];

	if ((dirp = opendir( path )) != 0)
		{
		struct direct const* dp;

		while ((dp = readdir(dirp)) != 0)
			{			// Do not include the "." (self) entry.
			int const len = dp->d_namlen;
			char const* const nam = dp->d_name;
			BOOL const dot_name = (nam[0] == '.');
			if ((len > 1 || !dot_name) &&		// exclude "."
				(showHidden || !dot_name ||
				(len == 2 && nam[1] == '.')))	// include ".."
				{
				memcpy( namebuff + dirlen, nam, len + 1 );
				[self addName:nam path:namebuff];
				}
			}

		closedir( dirp );
		}
	else
		{
		rc = errno;
		}

	return rc;
	}

@end
